package server_msg // Package msg 定义包名为msg

import (
	"encoding/binary" // 导入二进制数据处理包
	"errors"          // 导入错误处理包
	"fmt"

	"google.golang.org/protobuf/proto"                // 导入protobuf的proto包，用于序列化和反序列化
	"google.golang.org/protobuf/reflect/protoreflect" // 导入protobuf的反射包，用于描述消息结构
	"google.golang.org/protobuf/types/dynamicpb"      // 导入protobuf的动态消息包
)

// Decode 解码函数，将字节数组和消息码解码为protobuf动态消息
func Decode(msgData []byte, msgCode int16) (*dynamicpb.Message, error) {
	if nil == msgData {
		return nil, errors.New("消息数据为空") // 如果数据为空，返回错误
	}

	// 根据消息码获取对应的消息描述符
	msgDesc, err := GetMsgDescByMsgCode(msgCode)
	if err != nil {
		return nil, fmt.Errorf("获取消息描述符失败: %w", err)
	}

	// 检查消息描述符是否有效
	if msgDesc == nil {
		return nil, fmt.Errorf("消息码 %d 对应的描述符为空", msgCode)
	}

	// 通过消息描述构建一个新的动态消息对象
	newMsg := dynamicpb.NewMessage(msgDesc)

	// 使用proto包将字节数组反序列化到newMsg对象中
	if err := proto.Unmarshal(msgData, newMsg); err != nil {
		return nil, fmt.Errorf("消息反序列化失败: %w", err) // 反序列化失败，返回错误
	}

	return newMsg, nil // 返回解码后的消息对象和nil错误
}

// Encode 编码函数，将protobuf消息对象编码为字节数组
func Encode(msgObj protoreflect.ProtoMessage) ([]byte, error) {
	if nil == msgObj {
		return nil, errors.New("需要解码的消息对象为nil") // 如果消息对象为nil，返回错误
	}

	var msgCode int16 // 定义消息码变量，实际应赋值

	msgCode, err := GetMsgCodeByMsgName(string(msgObj.ProtoReflect().Descriptor().Name()))

	if nil != err {
		return nil, err // 序列化失败，返回错误
	}

	msgSizeByteArray := make([]byte, 2)             // 创建2字节数组用于存储消息长度
	binary.BigEndian.PutUint16(msgSizeByteArray, 0) // 先将消息度设为0（实际应填充真实长度）

	msgCodeByteArray := make([]byte, 2)                           // 创建2字节数组用于存储消息码
	binary.BigEndian.PutUint16(msgCodeByteArray, uint16(msgCode)) // 将消息码写入字节数组

	msgBodyArray, err := proto.Marshal(msgObj) // 将消息对象序列化为字节数组

	if nil != err {
		return nil, err // 序列化失败，返回错误
	}

	completeMsg := append(msgSizeByteArray, msgCodeByteArray...) // 先拼接长度和消息码
	completeMsg = append(completeMsg, msgBodyArray...)           // 再拼接消息体

	return completeMsg, nil // 返回完整消息和nil错误
}
